

#include <iostream>
#include <fstream>
#include <cassert>
#include <cstring>

#include "GLMesh.h"
#include "RayTracing.h"

#include "leak.h"

using namespace std;



void GLMesh::LoadFromFile(string file_name){

	ifstream in_file(file_name, ios::in | ios::binary);

	if(!in_file)
	{
		return;
	}

	char str[12];

	in_file.read(str, 12);
	if(strcmp(str, "STATIC_MESH\n") != 0)
	{
		assert("awaw");
	}

	enum{
		MESH_INFO_CHUNK = 0,
		VTX_ATTR_CHUNK,
		VTX_NDX_CHUNK,
		TEXTURES_CHUNK,
		STRIPS_CHUNK,
	};

	class ChunkPos{
	public:
		int offset;
		int length;
	};

	class Header{
	public:
		int num_chunks;
		ChunkPos p[5];
	}file_header;

	in_file.read((char*)&file_header, sizeof(file_header));

	char str1[32];
	in_file.read((char*)&str1, 32);
	name_ = string(str1);
	file_name_ = file_name;

	in_file.read((char*)&bbox_min_[0], 3 * sizeof(float));
	in_file.read((char*)&bbox_max_[0], 3 * sizeof(float));

	vtx_attribs_.resize(file_header.p[VTX_ATTR_CHUNK].length/4);
	in_file.read((char*)&vtx_attribs_[0], file_header.p[VTX_ATTR_CHUNK].length);

	vtx_indices_.resize(file_header.p[VTX_NDX_CHUNK].length/2);
	in_file.read((char*)&vtx_indices_[0], file_header.p[VTX_NDX_CHUNK].length);

	texture_names_.resize(file_header.p[TEXTURES_CHUNK].length/64);
	strips.resize(file_header.p[STRIPS_CHUNK].length/8);
	//tex_ids_.resize(file_header.p[TEXTURES_CHUNK].length/64);
	for(unsigned int i = 0; i < texture_names_.size(); i++)
	{
		char str2[64];
		in_file.read((char*)&str2[0], 64);
		texture_names_[i] = str2;
		cout << "texture: " << texture_names_[i] << endl;

		//strips[i].tex_id = Renderer::LoadTexture(texture_names_[i].c_str());
		strips[i].tex = Renderer::LoadTexture2(texture_names_[i].c_str());
		cout << "tex_id: " << *strips[i].tex << endl;
	}

	for(unsigned int i = 0; i < strips.size(); i++)
	{
		int index, num_indices;
		in_file.read((char*)&index, sizeof(int));
		in_file.read((char*)&num_indices, sizeof(int));
		strips[i].offset = index * sizeof(unsigned short);
		strips[i].num_indices = num_indices;
		cout << strips[i].offset << endl;
	}


	glGenBuffers(1, &attribs_buf_id_);
	glBindBuffer(GL_ARRAY_BUFFER, attribs_buf_id_);
	glBufferData(GL_ARRAY_BUFFER, vtx_attribs_.size() * sizeof(float), &vtx_attribs_[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ARRAY_BUFFER, 0);

	glGenBuffers( 1, &indices_buf_id_ );
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buf_id_);
	glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short)*vtx_indices_.size(), &vtx_indices_[0], GL_STATIC_DRAW);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

}

GLMesh::~GLMesh(){
	cout << "GLMesh destructor called" << endl;
	glDeleteBuffers(1, &attribs_buf_id_);
	glDeleteBuffers(1, &indices_buf_id_);
}

void GLMesh::CleanUp(){
	vtx_attribs_.clear();
	vtx_indices_.clear();
	texture_names_.clear();
}


void GLMesh::Draw(){

	glBindBuffer(GL_ARRAY_BUFFER, attribs_buf_id_);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indices_buf_id_);

	glEnableVertexAttribArray(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE));
	glVertexAttribPointer(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE), 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)0);

	glEnableVertexAttribArray(Renderer::render_state->current_program->attribute(NORMAL_ATTRIBUTE));
	glVertexAttribPointer(Renderer::render_state->current_program->attribute(NORMAL_ATTRIBUTE), 3, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(0 + 3*4));

	glEnableVertexAttribArray(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE));
	glVertexAttribPointer(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE), 2, GL_FLOAT, GL_FALSE, sizeof(float) * 8, (GLvoid*)(0 + 6*4));

	for(unsigned int i = 0; i < strips.size(); i++)
	{
		//Renderer::SwitchTexture(DIFFUSEMAP_SLOT, tex_ids_[i]);
		Renderer::SwitchOwnTexture(DIFFUSEMAP_SLOT, *strips[i].tex);
		glDrawElements(GL_TRIANGLE_STRIP, strips[i].num_indices, GL_UNSIGNED_SHORT, (GLvoid*)(strips[i].offset));
	}

	glDisableVertexAttribArray(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE));
	glDisableVertexAttribArray(Renderer::render_state->current_program->attribute(NORMAL_ATTRIBUTE));
	glDisableVertexAttribArray(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE));

	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	BaseObject::Draw();

}

bool GLMesh::TraceL(const float orig[3], const float dir[3], float* dist){

	if(!BaseObject::TraceL(orig, dir, dist))
	{
		return false;
	}

	float min_dist = numeric_limits<float>::max();
	bool b = false;
	for(auto index1 = vtx_indices_.begin(); index1 != vtx_indices_.end() - 2; index1++)
	{
		auto index2 = index1 + 1;
		auto index3 = index1 + 2;

		float tri[9];
		copy( &vtx_attribs_[(*index1)*8], &vtx_attribs_[(*index1)*8] + 3, tri );
		copy( &vtx_attribs_[(*index2)*8], &vtx_attribs_[(*index2)*8] + 3, tri + 3);
		copy( &vtx_attribs_[(*index3)*8], &vtx_attribs_[(*index3)*8] + 3, tri + 6);

		float d;
		if( TraceLine( orig, dir, tri, &d ) )
		{
			b = true;
			if( d < min_dist )
			{
				min_dist = d;
			}
		}
	}

	*dist = min_dist;
	return b;
}

/************************************************************************************************/

std::shared_ptr<GLMesh> LoadMeshFromFile(string file){
	auto iter = loaded_meshes.find(file);
	if(iter != loaded_meshes.end())
	{
		if(!(*iter).second.expired())
		{
			return iter->second.lock();
		}
	}

	std::shared_ptr<GLMesh> ret(make_shared<GLMesh>());
	ret->LoadFromFile(file);
	loaded_meshes[file] = ret;
	return ret;
}

/*void UnLoadMesh(string file){
	loaded_meshes.erase(file);
}*/

/************************************************************************************************/

bool BaseObject::TraceL(const float orig[3], const float dir[3], float* dist){

	float inv_dir[3] = {1/dir[0], 1/dir[1], 1/dir[2]};

	float low = inv_dir[0] * ( bbox_min_[0] - orig[0] );
	float high = inv_dir[0] * ( bbox_max_[0] - orig[0] );
	float tmin = min(low, high);
	float tmax = max(low, high);

	low = inv_dir[1] * ( bbox_min_[1] - orig[1] );
	high = inv_dir[1] * ( bbox_max_[1] - orig[1] );
	tmin = max( tmin, min( low, high ));
	tmax = min( tmax, max( low, high ));

	low = inv_dir[2] * ( bbox_min_[2] - orig[2] );
	high = inv_dir[2] * ( bbox_max_[2] - orig[2] );
	tmin = max( tmin, min( low, high ));
	tmax = min( tmax, max( low, high ));

	*dist = tmin;

	if((tmin > tmax)||(tmax <= 0))
	{
		//cout << "early exit" << endl;
		return false;
	}

	return true;
}

void BaseObject::Draw(){

	float lines[] = {	bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2]	};

	float uvs[48] = {};

	glEnableVertexAttribArray(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE));
	glVertexAttribPointer(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE), 3, GL_FLOAT, GL_FALSE, 0, &lines[0]);

	glEnableVertexAttribArray(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE));
	glVertexAttribPointer(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE), 2, GL_FLOAT, GL_FALSE, 0, &uvs[0]);

	glDrawArrays(GL_LINES, 0, 24);

	glDisableVertexAttribArray(Renderer::render_state->current_program->attribute(UV_ATTRIBUTE));
	glDisableVertexAttribArray(Renderer::render_state->current_program->attribute(POSITION_ATTRIBUTE));

	/*glBegin(GL_LINES);

		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_max_[2]);
		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_min_[1],	bbox_max_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_max_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_min_[0],	bbox_max_[1],	bbox_max_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_min_[2]);
		glVertex3f(bbox_max_[0],	bbox_max_[1],	bbox_max_[2]);

	glEnd();*/

}

/************************************************************************************************/

bool MovableObject::Check(float x, float y, glm::mat4x4& view_proj_mat, const float* dims, const int* dims_px, float* dist){

	float lines[] = {	bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2]	};

	glm::vec3 p = { x, y, 0.0f };
	//float p[3] = { x, y, 0.0f };

	float min_dist = std::numeric_limits<float>::max();

	for( int i = 0; i < 72; i += 3 * 2 )
	{
		glm::vec4 v1, v2;
		float z1, z2, res_vec[3];
		v1 = view_proj_mat * glm::vec4(lines[i], lines[i+1], lines[i+2], 1.0f);
		z1 = v1[2];
		v1[2] = 0.0f;

		v2 = view_proj_mat * glm::vec4(lines[i+3], lines[i+4], lines[i+5], 1.0f);
		z2 = v2[2];
		v2[2] = 0.0f;

		float dist = DistPointToSegment( &v1[0], &v2[0], &p[0], res_vec );
		float d = 0.5f * (dist / dims[2]) * dims_px[2];
		if( d < 6.0f )
		{
			float p_on_line[3] = {p[0] + res_vec[0], p[1] + res_vec[1], 0.0f};
			float t;
			if(abs(p_on_line[0] - v2[0]) > abs(p_on_line[1] - v2[1]))
			{
				t = (p_on_line[0] - v2[0])/(v1[0] - v2[0]);
			} else
			{
				t = (p_on_line[1] - v2[1])/(v1[1] - v2[1]);
			}
			p_on_line[2] = -(t * z1 + (1.0f - t)*z2);

			if(p_on_line[2] < min_dist)
			{
				//min_dist = dist;
				min_dist = p_on_line[2];
			}


		}
	}

	if(min_dist < std::numeric_limits<float>::max())
	{
		if(dist)
		{
			*dist = min_dist;
		}
		return true;
	} else
	{
		return false;
	}

}

void MovableObject::UpdateBBox(){

	bbox_min_ = bbox_max_ = glm::vec3(model_matrix_[3]);

	for(int j = 0; j < 3; j++)
	{
		for(int i = 0; i < 3; i++)
		{
			float a = model_matrix_[i][j] * orig_bbox_min_[i];
			float b = model_matrix_[i][j] * orig_bbox_max_[i];

			if(a < b)
			{
				bbox_min_[j] += a;
				bbox_max_[j] += b;
			} else
			{
				bbox_min_[j] += b;
				bbox_max_[j] += a;
			}
		}
	}

}

void MovableObject::SetPos(float x, float y, float z){
	model_matrix_[3] = glm::vec4(x, y, z, 1.0f);
	UpdateBBox();
}

void MovableObject::SetMatrix(glm::mat4x4 mat){
	model_matrix_ = mat;
}

void MovableObject::Move(float dx, float dy, float dz){
	model_matrix_ = glm::translate(model_matrix_, glm::vec3(dx, dy, dz));
	UpdateBBox();
}

void MovableObject::SetAngles(float x, float y, float z){
	glm::vec4 tr = model_matrix_[3];
	model_matrix_ = glm::mat4x4(1.0f);
	model_matrix_ = glm::rotate(model_matrix_, z, glm::vec3(0.0f, 0.0f, 1.0f));
	model_matrix_ = glm::rotate(model_matrix_, x, glm::vec3(1.0f, 0.0f, 0.0f));
	model_matrix_ = glm::rotate(model_matrix_, y, glm::vec3(0.0f, 1.0f, 0.0f));
	model_matrix_[3] = tr;
	UpdateBBox();
}

void MovableObject::Rotate(float dx, float dy, float dz){
	model_matrix_ = glm::rotate(model_matrix_, dz, glm::vec3(0.0f, 0.0f, 1.0f));
	model_matrix_ = glm::rotate(model_matrix_, dx, glm::vec3(1.0f, 0.0f, 0.0f));
	model_matrix_ = glm::rotate(model_matrix_, dy, glm::vec3(0.0f, 1.0f, 0.0f));
	UpdateBBox();
}

void MovableObject::Rotate(glm::vec3 axis, float angle){
	model_matrix_ = glm::rotate(model_matrix_, angle, axis);
	UpdateBBox();
}

void MovableObject::RotateX(float dx){
	model_matrix_ = glm::rotate(model_matrix_, dx, glm::vec3(1.0f, 0.0f, 0.0f));
	UpdateBBox();
}

void MovableObject::RotateY(float dy){
	model_matrix_ = glm::rotate(model_matrix_, dy, glm::vec3(0.0f, 1.0f, 0.0f));
	UpdateBBox();
}

void MovableObject::RotateZ(float dz){
	model_matrix_ = glm::rotate(model_matrix_, dz, glm::vec3(0.0f, 0.0f, 1.0f));
	UpdateBBox();
}

void MovableObject::Rescale( const float old_box_min[3], const float old_box_max[3], const float new_box_min[3], const float new_box_max[3] ) {

	glm::vec3 d = {	new_box_max[0] - old_box_max[0],
					new_box_max[1] - old_box_max[1],
					new_box_max[2] - old_box_max[2]	};

	glm::vec4 dd = {d[0], d[1], d[2], 1.0f};
	d[0] = glm::dot(dd, model_matrix_[0]);
	d[1] = glm::dot(dd, model_matrix_[1]);
	d[2] = glm::dot(dd, model_matrix_[2]);

	cout << "movee " << d[0] << " " << d[1] << " " << d[2] << endl;
	Move(d[0], d[1], d[2]);
}

/************************************************************************************************/

int GLStaticMesh::num_meshes = 0;

void GLStaticMesh::LoadFromFile(string file_name){
	p_mesh_ = LoadMeshFromFile(file_name);
	if(name_.length() == 0)
	{
		name_ = p_mesh_->name() + "_" + std::to_string(num_meshes);
	}
	num_meshes++;

	orig_bbox_min_ = p_mesh_->bbox_min();
	orig_bbox_max_ = p_mesh_->bbox_max();

	UpdateBBox();

}

void GLStaticMesh::Draw(){
	glm::mat4x4 model_view_proj_mat;
	model_view_proj_mat = glm::make_mat4(Renderer::render_state->current_cam->view_matrix()) * model_matrix_;
	model_view_proj_mat = glm::make_mat4(Renderer::render_state->current_cam->projection_matrix()) * model_view_proj_mat;

	glUniformMatrix4fv(Renderer::render_state->current_program->uniform(MVP_UNIFORM), 1, GL_FALSE, &model_view_proj_mat[0][0]);

	p_mesh_->Draw();
}
/*
void GLStaticMesh::UpdateBBox(){
	auto min = p_mesh_->bbox_min();
	auto max = p_mesh_->bbox_max();
	glm::vec4 p1 = model_matrix_ * glm::vec4(min[0], min[1], min[2], 1.0f);
	glm::vec4 p2 = model_matrix_ * glm::vec4(max[0], min[1], min[2], 1.0f);
	glm::vec4 p3 = model_matrix_ * glm::vec4(min[0], min[1], max[2], 1.0f);
	glm::vec4 p4 = model_matrix_ * glm::vec4(max[0], min[1], max[2], 1.0f);

	glm::vec4 p5 = model_matrix_ * glm::vec4(min[0], max[1], min[2], 1.0f);
	glm::vec4 p6 = model_matrix_ * glm::vec4(max[0], max[1], min[2], 1.0f);
	glm::vec4 p7 = model_matrix_ * glm::vec4(min[0], max[1], max[2], 1.0f);
	glm::vec4 p8 = model_matrix_ * glm::vec4(max[0], max[1], max[2], 1.0f);

	//glm::vec4 new_bbox_min = model_matrix_ * glm::vec4(p_mesh_->bbox_min()[0], p_mesh_->bbox_min()[1], p_mesh_->bbox_min()[2], 1.0f);
	//glm::vec4 new_bbox_max = model_matrix_ * glm::vec4(p_mesh_->bbox_max()[0], p_mesh_->bbox_max()[1], p_mesh_->bbox_max()[2], 1.0f);
	bbox_min_[0] = glm::min(glm::min(glm::min(p1[0], p2[0]), glm::min(p3[0], p4[0])), glm::min(glm::min(p5[0], p6[0]), glm::min(p7[0], p8[0])));
	bbox_min_[1] = glm::min(glm::min(glm::min(p1[1], p2[1]), glm::min(p3[1], p4[1])), glm::min(glm::min(p5[1], p6[1]), glm::min(p7[1], p8[1])));
	bbox_min_[2] = glm::min(glm::min(glm::min(p1[2], p2[2]), glm::min(p3[2], p4[2])), glm::min(glm::min(p5[2], p6[2]), glm::min(p7[2], p8[2])));
	bbox_max_[0] = glm::max(glm::max(glm::max(p1[0], p2[0]), glm::max(p3[0], p4[0])), glm::max(glm::max(p5[0], p6[0]), glm::max(p7[0], p8[0])));
	bbox_max_[1] = glm::max(glm::max(glm::max(p1[1], p2[1]), glm::max(p3[1], p4[1])), glm::max(glm::max(p5[1], p6[1]), glm::max(p7[1], p8[1])));
	bbox_max_[2] = glm::max(glm::max(glm::max(p1[2], p2[2]), glm::max(p3[2], p4[2])), glm::max(glm::max(p5[2], p6[2]), glm::max(p7[2], p8[2])));
}
*/
/*
bool GLStaticMesh::Check(float x, float y, glm::mat4x4& view_proj_mat, MyBaseElement& element){

	cout << bbox_min_[0] << " " << bbox_min_[1] << " " << bbox_min_[2] << endl;

	float lines[] = {	bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_min_[1], bbox_min_[2],
						bbox_min_[0], bbox_min_[1], bbox_max_[2],
						bbox_max_[0], bbox_min_[1], bbox_min_[2],
						bbox_max_[0], bbox_min_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2],
						bbox_min_[0], bbox_max_[1], bbox_min_[2],
						bbox_min_[0], bbox_max_[1], bbox_max_[2],
						bbox_max_[0], bbox_max_[1], bbox_min_[2],
						bbox_max_[0], bbox_max_[1], bbox_max_[2]	};

	glm::vec3 p = { x, y, 0.0f };
	//float p[3] = { x, y, 0.0f };

	for( int i = 0; i < 72; i += 3 * 2 )
	{
		glm::vec4 v1, v2;
		v1 = view_proj_mat * glm::vec4(lines[i], lines[i+1], lines[i+2], 1.0f);
		v1[2] = 0.0f;

		v2 = view_proj_mat * glm::vec4(lines[i+3], lines[i+4], lines[i+5], 1.0f);
		v2[2] = 0.0f;

		float dist = DistPointToSegment( &v1[0], &v2[0], &p[0], nullptr );
		float d = 0.5f * (dist / element.dims()[2]) * element.dims_px()[2];
		if( d < 6.0f )
		{
			return true;
		}
	}
	return false;
}
*/
bool GLStaticMesh::TraceL(const float orig[3], const float dir[3], float* dist){
	glm::mat4x4 inv_matr = glm::inverse(model_matrix_);
	glm::vec4 origin = inv_matr * glm::vec4(orig[0], orig[1], orig[2], 1.0f);
	glm::vec4 direction = inv_matr * glm::vec4(dir[0], dir[1], dir[2], 0.0f);
	return p_mesh_->TraceL(&origin[0], &direction[0], dist);
}


/************************************************************************************************/

float DistPointToPoint( float p1[], float p2[], float* res_vec ) {

	float vec[3] = {	p2[0] - p1[0],
						p2[1] - p1[1],
						p2[2] - p1[2]	};

	if( res_vec )
	{
		std::copy( vec, vec + 3, res_vec );
	}

	return sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);

}

float DistPointToSegment( float v1[], float v2[], float p[], float* res_vec ) {

	float v[3] = { v2[0] - v1[0], v2[1] - v1[1], v2[2] - v1[2] };
	float w[3] = { p[0] - v1[0], p[1] - v1[1], p[2] - v1[2] };
	float vec[3];

	float c1 = v[0]*w[0] + v[1]*w[1] + v[2]*w[2];
	if( c1 <= 0 )
	{
		vec[0] = -w[0];
		vec[1] = -w[1];
		vec[2] = -w[2];
		if( res_vec )
		{
			std::copy( vec, vec + 3, res_vec );
		}
		return sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
	}

	float c2 = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
	if( c2 <= c1 )
	{
		vec[0] = p[0] - v2[0];
		vec[1] = p[1] - v2[1];
		vec[2] = p[2] - v2[2];
		if( res_vec )
		{
			std::copy( vec, vec + 3, res_vec );
		}
		return sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);
	}

	float b = c1 / c2;
	float Pb[3] = { v1[0] + b * v[0], v1[1] + b * v[1], v1[2] + b * v[2] };
	vec[0] = Pb[0] - p[0];
	vec[1] = Pb[1] - p[1];
	vec[2] = Pb[2] - p[2];
	if( res_vec )
	{
		std::copy( vec, vec + 3, res_vec );
	}
	return sqrt(vec[0]*vec[0] + vec[1]*vec[1] + vec[2]*vec[2]);

}

